#include <boost/optional.hpp>


#include <kuai/misc/constant.hpp>
#include <kuai/mol/space.hpp>
#include <kuai/sim/studio.hpp>

#ifndef _KUAI_SIM_PVT_HPP_
#define _KUAI_SIM_PVT_HPP_

namespace kuai {

	class PropertyTemperature0
		: public SingleProperty<RealNumber>
	{ 
	public:
		PropertyTemperature0(PhaseSpace& space);

		virtual String get(const String& key) const;

		inline RealNumber kinetic_energy() const {
			return SI::BOLTZMANN_CONSTANT * SI::AVOGADRO_CONSTANT / 1000 / 2 * c_ref() * (3 * nAtoms - nConstrains);
		}

	protected:
		Index nAtoms;
		Index nConstrains;
	};

	class PropertyTemperature
		: public PropertyTemperature0, public SpaceControl
	{
	public:
		PropertyTemperature(PhaseSpace& space);

		void calcTemperature(SimulationEnvironment& env);
		void scaleSpeed(SimulationEnvironment& env, RealNumber factor);
		void resetTemperature(SimulationEnvironment& env, RealNumber target);

		virtual void start(SimulationEnvironment& env, PhaseSpace& space, EnergySet& efunc);
		virtual void act(SimulationEnvironment& env, size_t iStep, PhaseSpace& space, EnergySet& efunc);

		virtual void set(const String& key, const String& data);
		virtual String get(const String& key) const;


	protected:
		XYZ* speeds;
		RealNumber* weights;

		boost::optional<RealNumber> init;

	};

	class TemperatureControl
		: public PropertyTemperature
	{
	public:
		TemperatureControl(PhaseSpace& space)
			: PropertyTemperature(space)
		{ 
			delta = 0;
			target0 = target = 300;
		}

		virtual void start(SimulationEnvironment& env, PhaseSpace& space, EnergySet& efunc);
		virtual void set(const String& key, const String& data);
		virtual String get(const String& key) const;

	protected:
		RealNumber target0;
		RealNumber target;
		RealNumber delta;
	};

	class BrendsonTemperatureControl
		: public TemperatureControl
	{
	public:
		BrendsonTemperatureControl(PhaseSpace& space);

		virtual void act(SimulationEnvironment& env, size_t iStep, PhaseSpace& space, EnergySet& efunc);

		virtual void set(const String& key, const String& data);
		virtual String get(const String& key) const;

	protected:
		RealNumber compressibility;
		RealNumber factor;
	};

	class AndersonTemperatureControl
		: public TemperatureControl
	{
	public:
		AndersonTemperatureControl(PhaseSpace& space) 
			: TemperatureControl(space)
		{
			possibility = 1;	
		};

		virtual void act(SimulationEnvironment& env, size_t iStep, PhaseSpace& space, EnergySet& efunc);

		virtual void set(const String& key, const String& data);
		virtual String get(const String& key) const;

	protected:
		RealNumber possibility;
		RealNumber die;
	};

	class PressureProperty0
		: public SingleProperty<XYZ>
	{
	public:
		explicit PressureProperty0(PhaseSpace& space);
		virtual String get(const String& key) const;
		virtual void set(const String& key, const String& data);
	};

	class PressureProperty
		: public PressureProperty0, public SpaceControl
	{
	public:
		explicit PressureProperty(PhaseSpace& space);
		void calcPressure(SimulationEnvironment& env);
		void scaleBox(SimulationEnvironment& env, RealNumber factorX, RealNumber factorY, RealNumber factorZ);

		virtual void start(SimulationEnvironment& env, PhaseSpace& space, EnergySet& efunc);
		virtual void act(SimulationEnvironment& env, size_t iStep, PhaseSpace& space, EnergySet& efunc);

		virtual void set(const String& key, const String& data);
		virtual String get(const String& key) const;

	protected:
		PropertyTemperature0* temperature;
		RealNumber tail_p;
		XYZ* coords;
		XYZ* forces;
	};

}

#endif
